﻿using FanaticRock.MediaSource.BilibiliAPI.Models;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace FanaticRock.MediaSource.BilibiliAPI.Helpers
{
    ///<summary>
    /// 代码来源 https://socialsisteryi.github.io/bilibili-API-collect/docs/misc/sign/wbi.html#csharp
    ///</summary>
    public partial class WbiHelper
    {
        ///<summary>存在内存中的Wbi信息</summary>
        private static Wbi _tempWbi = null;

        private static readonly int[] MixinKeyEncTab =
        {
            46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39,
            12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63,
            57, 62, 11, 36, 20, 34, 44, 52
        };

        //对 imgKey 和 subKey 进行字符顺序打乱编码
        private static string GetMixinKey(string orig)
        {
            return MixinKeyEncTab.Aggregate("", (s, i) => s + orig[i])[..32];
        }

        public static async Task<Dictionary<string, string>> EncWbi(Dictionary<string, string> parameters)
        {
            var wbi = await GetWbiKeys();
            return  EncWbi(parameters, wbi.ImgKey, wbi.SubKey);
        }

        public static Dictionary<string, string> EncWbi(Dictionary<string, string> parameters, Wbi wbi)
        {
            return EncWbi(parameters, wbi.ImgKey, wbi.SubKey);
        }

        public static Dictionary<string, string> EncWbi(Dictionary<string, string> parameters, string imgKey,
            string subKey)
        {
            string mixinKey = GetMixinKey(imgKey + subKey);
            string currTime = DateTimeOffset.Now.ToUnixTimeSeconds().ToString();
            //添加 wts 字段
            parameters["wts"] = currTime;
            // 按照 key 重排参数
            parameters = parameters.OrderBy(p => p.Key).ToDictionary(p => p.Key, p => p.Value);
            //过滤 value 中的 "!'()*" 字符
            parameters = parameters.ToDictionary(
                kvp => kvp.Key,
                kvp => new string(kvp.Value.Where(chr => !"!'()*".Contains(chr)).ToArray())
            );
            // 序列化参数
            string query = new FormUrlEncodedContent(parameters).ReadAsStringAsync().Result;
            //计算 w_rid
            using MD5 md5 = MD5.Create();
            byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(query + mixinKey));
            string wbiSign = BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
            parameters["w_rid"] = wbiSign;

            return parameters;
        }

        // 获取最新的 img_key 和 sub_key
        private static async Task<Wbi> CreateWbiKeysCore()
        {
            var time = DateTime.Now;
            var httpClient = HttpClientHelper.GetHttpClient();
            HttpResponseMessage responseMessage = await httpClient.SendAsync(new HttpRequestMessage
            {
                Method = HttpMethod.Get,
                RequestUri = new Uri("https://api.bilibili.com/x/web-interface/nav"),
            });

            JsonNode response = JsonNode.Parse(await responseMessage.Content.ReadAsStringAsync())!;

            string imgUrl = (string)response["data"]!["wbi_img"]!["img_url"]!;
            imgUrl = imgUrl.Split("/")[^1].Split(".")[0];

            string subUrl = (string)response["data"]!["wbi_img"]!["sub_url"]!;
            subUrl = subUrl.Split("/")[^1].Split(".")[0];

            return new Wbi()
            {
                ImgKey = imgUrl,
                SubKey = subUrl,
                CreateTime = time,
            };
        }

        private static async Task<Wbi> CreateWbiKeys()
        {
            var wbi = await CreateWbiKeysCore();
            wbi.CreateTime = DateTime.Now;
            return wbi;
        }

        ///<summary>获得本地存储的 test.wbidata </summary>
        private static string GetWbiKeysInLocalPath()
        {
            var root = AppDomain.CurrentDomain.BaseDirectory;
            var path = Path.Combine(root, "test.wbidata");
            return path;
        }

        ///<summary>获得本地的日期存储。</summary>
        private static Wbi GetWbiKeysInLocal()
        {
            var path = GetWbiKeysInLocalPath();
            if (File.Exists(path)) return null;
            try
            {
                return JsonSerializer.Deserialize<Wbi>(path);
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        public static async Task<Wbi> GetWbiKeys()
        {
            var wbi = GetWbiKeysInLocal();

            if (IsOutdate(wbi))
            {
                wbi = await CreateWbiKeys();
                SaveWbi(wbi);
            }

            return wbi;
        }

        public static async Task<Wbi> GetWbiKeysForce()
        {
            var wbi = await CreateWbiKeys();
            SaveWbi(wbi);
            return wbi;
        }

        private static void SaveWbi(Wbi wbi)
        {
            _tempWbi = wbi;
            var path = GetWbiKeysInLocalPath();
            using var fs = new FileStream(path, FileMode.Create, FileAccess.Write);
            JsonSerializer.Serialize(fs, wbi);
        }

        private static bool IsOutdate(Wbi wbi)
        {
            if (wbi == null) return true;
            var nowTime = DateTime.Now;
            var createTime = wbi.CreateTime.Date;

            var outdateTime = createTime.AddDays(1);
            if (nowTime > outdateTime) return true;
            return false;
        }

        //public static async Task Test()
        //{
        //    var (imgKey, subKey) = await GetWbiKeys();

        //    Dictionary<string, string> signedParams = EncWbi(
        //        ,
        //        imgKey: imgKey,
        //        subKey: subKey
        //    );

        //    string query = await new FormUrlEncodedContent(signedParams).ReadAsStringAsync();

        //    Console.WriteLine(query);
        //}
    }
}
